/**
 *  @基于AXIOS二次封装请求方法
 *
 */

import axios from 'axios'
import { Message } from 'element-ui'
import { removeToken } from '@/utils/auth'
import store from '@/store'
import router from '@/router'
import Vue from 'vue'
// 创建 axios 实例
const service = axios.create({
    baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
    // 跨域请求时是否需要使用凭证
    withCredentials: false,
    // 请求超时
    // timeout: 3000,
    // 服务器响应的数据类型
    responseType: 'json'
})

let pending = [] // 声明一个数组用于存储每个axios请求的取消函数和axios标识
let cancelToken = axios.CancelToken
let removePending = (ever) => {
    for (let p in pending) {
        if (pending[p].u === ever.url + '&' + ever.method) { //当当前请求在数组中存在时执行函数体
            pending[p].f() //执行取消操作
            pending.splice(p, 1) //把这条记录从数组中移除
        }
    }
}
// 设置post请求头
service.defaults.headers.post['Access-Control-Allow-Headers'] = 'X-Requested-With'
service.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8'

// 请求拦截器
service.interceptors.request.use(
    (config) => {

        // 每次发送请求之前判断是否存在token，如果存在，则统一在http请求的header都加上token，不用每次请求都手动添加了
        // 即使本地存在token，也有可能token是过期的，所以在响应拦截器中要对返回状态进行判断
        // 或者 自定义设置key-value
        const token = store.getters.token
        token && (config.headers.Authorization = token)
        // -------------------------------- 存储请求体  ---------------------------------------------------
        // 移除相同点击
        removePending(config)
        config.cancelToken = new cancelToken((c) => {
            // 这里的axios标识我是用请求地址&请求方式拼接的字符串，当然你可以选择其他的一些方式
            pending.push({ u: config.url + '&' + config.method, f: c })
        })
        // -----------------------------------------------------------------------------------------
        return config
    },
    (error) => {
        return Promise.reject(error)
    }
)

// 响应拦截器
service.interceptors.response.use(
    (response) => {
        // 特殊处理
        if (response.data.code && response.data.code === 40405) {
            resetToLogin()
            return {
                status: 200,
                obj: null
            }
        }
        removePending(response.config)  //在一个axios响应后再执行一下取消操作，把已经完成的请求从pending中移除
        return response.data
    },
    async (error) => {
        Message.closeAll()
        // 捕捉error后关闭Loading
        if (Vue.prototype.$customLoading) {
            Vue.prototype.$endLoading()
        }
        // 请求取消时，也会进入error，根据service.isCancel()：true--请求取消  false--请求失败
        if (axios.isCancel(error)) return Promise.reject(error)
        // 特殊处理 登录到期
        if (error.code === 40405) {
            resetToLogin(error.code)
            return Promise.reject(error)
        }
        // 捕捉error 抛出 message
        if (error && error.response) {
            switch (error.response.status) {
                case 400:
                    error.message = `${ error.response.data.path }请求错误(400)`
                    break
                case 401:
                    error.message = `未授权(401)`
                    resetToLogin()
                    break
                // 403 token过期
                case 403:
                    error.message = 'base.WU，请重新登录(403)'
                    resetToLogin()
                    break
                case 404:
                    error.message = `${ error.response.data.path }请求不存在(404)`
                    break
                case 500:
                    error.message = '网络不可用，请检查你的网络设置(500)'
                    break
                // 其他错误，直接抛出错误提示
                default:
                    console.log(error.response)
                    error.message = `${ error.response.data.path }${ error.response.data.message }!`
                    break
            }
        }
        // 抛出超时message
        if (!error.response) {
            error.message = error.message && error.message.includes('timeout') ? '请求超时!' : '请求失败!'
        }
        Message({
            message: error.message,
            type: 'error',
            duration: 5 * 1000,
            showClose: true
        })
        return Promise.reject(error)
    }
)
const resetToLogin = async () => {
    // 清除token
    removeToken()
    await store.dispatch('user/resetToken')
    // 跳转登录页面，并将要浏览的页面fullPath传过去，登录成功后跳转需要访问的页面
    Message.closeAll()
    Message({
        message: '登录过期，请重新登录(403)',
        type: 'error',
        duration: 5 * 1000,
        showClose: true
    })
    router.replace({
        path: '/login',
        query: {
            redirect: router.currentRoute.fullPath
        }
    })

}
export default service
export const axiosPromiseArr = pending
